package com.gitee.wsl.ext.map.bikey

import com.gitee.wsl.collections.map.bikey.BiKey
import com.gitee.wsl.func.BiFunction
import com.gitee.wsl.func.TriFunction


/**
 * Attempts to compute a mapping for the specified bikey and its current
 * mapped value or `null` if there is no current mapping.
 *
 *
 *
 * If the function returns `null`, the mapping is removed (or remains
 * absent if initially absent). If the function itself throws an (unchecked)
 * exception, the exception is rethrown, and the current mapping is left
 * unchanged.
 *
 * @param row
 * row with which the specified value is to be associated
 * @param column
 * column with which the specified value is to be associated
 * @param remappingFunction
 * the function to compute a value
 * @return the new value associated with the specified bikey, or null if
 * none
 * @throws NullPointerException
 * if the specified key is null or the remappingFunction is null
 */
fun <R, C, V> MutableMap<BiKey<R, C>,V>.compute(row: R, column: C, remappingFunction: TriFunction<in R, in C, in V?, out V?>): V? {
    val oldValue = get(row, column)
    val newValue = remappingFunction.apply(row, column, oldValue)
    if (newValue != null) {
        put(row, column, newValue)
        return newValue
    }
    if (oldValue != null) {
        remove(row, column)
    }
    return null
}

/**
 * If the specified bikey is not already associated with a value, attempts
 * to compute its value using the given mapping function and enters it into
 * this map unless `null`.
 *
 *
 *
 * If the mapping function returns `null`, no mapping is recorded. If
 * the mapping function itself throws an (unchecked) exception, the
 * exception is rethrown, and no mapping is recorded. The mapping function
 * should not modify this map during computation.
 *
 * @param row
 * row with which the specified value is to be associated
 * @param column
 * column with which the specified value is to be associated
 * @param mappingFunction
 * the mapping function to compute a value
 * @return the current (existing or computed) value associated with the
 * specified bikey, or null if the computed value is null
 * @throws NullPointerException
 * if the specified bikey is null or the mappingFunction is null
 */
fun <R, C, V> MutableMap<BiKey<R, C>,V>.computeIfAbsent(row: R, column: C, mappingFunction: BiFunction<R, C, out V?>): V? {
    val v: V? = get(row, column)
    if (v == null) {
        val newValue: V? = mappingFunction.apply(row, column)
        if (newValue != null) {
            put(row, column, newValue)
            return newValue
        }
    }
    return v
}

/**
 * If the value for the specified bikey is present and non-null, attempts to
 * compute a new mapping given the bikey and its current mapped value.
 *
 *
 *
 * If the remapping function returns `null`, the mapping is removed.
 * If the remapping function itself throws an (unchecked) exception, the
 * exception is rethrown, and the current mapping is left unchanged.
 *
 *
 *
 * The remapping function should not modify this map during computation.
 *
 * @param row
 * row with which the specified value is to be associated
 * @param column
 * column with which the specified value is to be associated
 * @param remappingFunction
 * the remapping function to compute a value
 * @return the new value associated with the specified key, or null if none
 * @throws NullPointerException
 * if the specified bikey is null or the mappingFunction is null
 */
fun <R, C, V> MutableMap<BiKey<R, C>,V>.computeIfPresent(
    row: R, column: C,
    remappingFunction: TriFunction<in R, in C, in V?, out V?>,
): V? {
    val oldValue: V? = get(row, column)
    if (oldValue != null) {
        val newValue: V? = remappingFunction.apply(row, column, oldValue)
        if (newValue != null) {
            put(row, column, newValue)
            return newValue
        }
        remove(row, column)
    }
    return null
}
